package com.geor.gcv.core.process;

import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Mat;

import com.geor.gcv.core.mat.Mats;

public class ThinProcss extends Process {
	private Process process;
	private Mat mat;

	public ThinProcss(Process process) {
		this(process.process());
		this.process = process;
	}

	public ThinProcss(Mat mat) {
		super();
		this.mat = mat;
	}

	@Override
	public String getProcessName() {
		return process.getProcessName() + "-->细化图片";
	}

	@Override
	public Mat process() {
		if (mat.channels() == 1)
			return thinExtract(mat);
		return mat;
	}

	// 细化函数；
	public Mat thinExtract(Mat mat) {
		int w = mat.width();
		int h = mat.height();
		int thin[][] = new int[h][w];

		for (int row = 0; row < mat.rows(); row++) {
			for (int col = 0; col < mat.cols(); col++) {
				thin[row][col] = (int) mat.get(row, col)[0] < 100 ? 0 : 1;
			}
		}

		List<Point> mark = new ArrayList<Point>();// 用于标记要删除的点的x和y坐标；
		boolean IsModified = true;
		@SuppressWarnings("unused")
		int Counter = 1;
		int[] nnb = new int[8];
		// 去掉边框像素
		for (int i = 0; i < w; i++) {
			thin[0][i] = 0;
			thin[h - 1][i] = 0;
		}
		for (int i = 0; i < h; i++) {
			thin[i][0] = 0;
			thin[i][w - 1] = 0;
		}

		do {
			Counter++;
			IsModified = false;
			// 每次周期循环判断前，先将数组中被标记的点变成0；
			for (int i = 0; i < mark.size(); i++) {
				Point p = mark.get(i);
				thin[p.getX()][p.getY()] = 0;
			}
			mark.clear();// 将向量清空
			int[][] nb = new int[3][3];
			for (int i = 0; i < h; i++) {
				for (int j = 0; j < w; j++) {
					// 条件1必须为黑点
					if (thin[i][j] != 1)
						continue;
					// 赋值3*3领域
					for (int m = 0; m < 3; m++) {
						for (int n = 0; n < 3; n++) {
							nb[m][n] = thin[i - 1 + m][j - 1 + n];
						}
					}
					// 复制
					nnb[0] = nb[1][2];
					nnb[1] = nb[0][2];
					nnb[2] = nb[0][1];
					nnb[3] = nb[0][0];
					nnb[4] = nb[1][0];
					nnb[5] = nb[2][0];
					nnb[6] = nb[2][1];
					nnb[7] = nb[2][2];
					// 条件2：p0,p2,p4,p6 不皆为前景点 ，4邻域点不能全为1；
					if (nnb[0] == 1 && nnb[2] == 1 && nnb[4] == 1 && nnb[6] == 1) {
						continue;
					}
					// 条件3: p0~p7至少两个是前景点 ，8邻域至少有2个点为1；
					int icount = 0;
					for (int ii = 0; ii < 8; ii++) {
						icount += nnb[ii];
					}
					if (icount < 2) {
						continue;
					}
					// 条件4：联结数等于1
					if (1 != detectConnectivity(nnb)) {
						continue;
					}
					// 条件5: 假设p2已标记删除，则令p2为背景，不改变p的联结数
					Point p2 = new Point(i - 1, j);
					if (mark.indexOf(p2) != -1) // 如果在向量mark中找到点p2
					{
						nnb[2] = 0;
						if (1 != detectConnectivity(nnb)) {
							nnb[2] = 1;
							continue;
						}
						nnb[2] = 1;
					}

					// 条件6: 假设p4已标记删除，则令p4为背景，不改变p的联结数
					Point p4 = new Point(i, j - 1);
					if (mark.indexOf(p4) == -1) // 如果p4没有标记将p点标记
					{
						Point p = new Point(i, j);
						mark.add(p);
						IsModified = true;
						continue;
					}

					// 如果p4 标记了，先把点p4变成0
					nnb[4] = 0;
					if (1 == detectConnectivity(nnb)) {// 如果p的连接数没有改变；将p标记
						Point p = new Point(i, j);
						mark.add(p);
						IsModified = true;
					}

				}
			}
		} while (IsModified);

		Mat img = new Mat(mat.size(), mat.type());
		for (int row = 0; row < mat.rows(); row++) {
			for (int col = 0; col < mat.cols(); col++) {
				img.put(row, col, thin[row][col] == 1 ? Mats.PEX_WHITE : Mats.PEX_BLACK);
			}
		}

		return img;
	}

	// 本函数用于实现条件4，判断8联通节点数；
	public int detectConnectivity(int[] a) {
		int size = 0;
		int a0, a1, a2, a3, a4, a5, a6, a7;
		a0 = 1 - a[0];
		a1 = 1 - a[1];
		a2 = 1 - a[2];
		a3 = 1 - a[3];
		a4 = 1 - a[4];
		a5 = 1 - a[5];
		a6 = 1 - a[6];
		a7 = 1 - a[7];
		size = a0 - a0 * a1 * a2 + a2 - a2 * a3 * a4 + a4 - a4 * a5 * a6 + a6 - a6 * a7 * a0;
		return size;
	}
	
	class Point {
		private int x;
		private int y;

		public Point(int x, int y) {
			super();
			this.x = x;
			this.y = y;
		}

		public int getX() {
			return x;
		}

		public void setX(int x) {
			this.x = x;
		}

		public int getY() {
			return y;
		}

		public void setY(int y) {
			this.y = y;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + x;
			result = prime * result + y;
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Point other = (Point) obj;
			if (x != other.x)
				return false;
			if (y != other.y)
				return false;
			return true;
		}

		@Override
		public String toString() {
			return "(" + x + "," + y + ")";
		}

		public Point clone() {
			return new Point(getX(), getY());
		}

	}

}
